#!/usr/bin/env python3
"""
数据库配置管理模块

本模块负责管理 MySQL 数据库连接配置，支持自定义端口设置和环境变量解析。
主要功能包括：
- 从环境变量加载配置
- 端口号验证
- 配置参数校验
- 默认值处理
"""

import os
import json
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, field
from dotenv import load_dotenv


class ConfigurationError(Exception):
    """
    配置错误异常
    
    当配置验证失败或配置不正确时抛出此异常。
    """
    
    def __init__(self, message: str, field: Optional[str] = None):
        """
        初始化配置错误
        
        Args:
            message: 错误消息
            field: 导致错误的配置字段
        """
        self.message = message
        self.field = field
        super().__init__(message)


@dataclass
class DatabaseConfig:
    """
    数据库连接配置类
    
    管理所有 MySQL 数据库连接相关的配置参数，支持从环境变量加载配置。
    包含端口验证、超时设置、连接池配置等功能。
    """
    
    host: str = field(default="localhost")
    port: int = field(default=3306)
    user: Optional[str] = field(default="")
    password: Optional[str] = field(default="")
    database: Optional[str] = field(default="")
    charset: str = field(default="utf8mb4")
    autocommit: bool = field(default=True)
    pool_size: int = field(default=5)
    max_overflow: int = field(default=10)
    pool_timeout: int = field(default=30)
    pool_recycle: int = field(default=3600)
    connection_timeout: int = field(default=30)
    max_connections: int = field(default=10)
    retry_count: int = field(default=3)
    retry_delay: int = field(default=1)
    ssl_disabled: bool = field(default=False)
    ssl_ca: Optional[str] = field(default=None)
    ssl_cert: Optional[str] = field(default=None)
    ssl_key: Optional[str] = field(default=None)
    
    def __post_init__(self):
        """
        初始化后的验证处理
        
        验证配置参数的有效性，特别是端口号的范围检查。
        """
        # 验证端口号
        if not (1 <= self.port <= 65535):
            raise ValueError("端口号必须在 1-65535 范围内")
    
    def validate(self) -> List[str]:
        """
        验证配置参数的有效性
        
        Returns:
            List[str]: 验证错误信息列表，空列表表示验证通过
        """
        errors = []
        
        # 验证必需字段
        if not self.database:
            errors.append("数据库名称不能为空")
        if not self.user:
            errors.append("用户名不能为空")
        if not self.password:
            errors.append("密码不能为空")
        
        # 验证端口号
        if not (1 <= self.port <= 65535):
            errors.append(f"端口号必须在 1-65535 范围内，当前值: {self.port}")
        
        # 验证连接池大小
        if self.pool_size <= 0:
            errors.append("连接池大小必须大于 0")
        
        # 验证其他数值字段
        if self.connection_timeout <= 0:
            errors.append("连接超时时间必须大于 0")
        
        if self.max_connections <= 0:
            errors.append("最大连接数必须大于 0")
        
        if self.retry_count < 0:
            errors.append("重试次数不能为负数")
        
        if self.retry_delay < 0:
            errors.append("重试延迟不能为负数")
        
        return errors
    
    @classmethod
    def from_env(cls, env_file: Optional[str] = None) -> 'DatabaseConfig':
        """
        从环境变量加载配置
        
        Args:
            env_file: .env 文件路径，如果为 None 则使用默认路径
            
        Returns:
            DatabaseConfig: 配置实例
        """
        # 加载 .env 文件
        if env_file:
            load_dotenv(env_file)
        else:
            load_dotenv()
        
        return cls(
            host=os.getenv('MYSQL_HOST', 'localhost'),
            port=int(os.getenv('MYSQL_PORT', '3306')),
            user=os.getenv('MYSQL_USER'),
            password=os.getenv('MYSQL_PASSWORD'),
            database=os.getenv('MYSQL_DATABASE'),
            connection_timeout=int(os.getenv('MYSQL_TIMEOUT', '30')),
            max_connections=int(os.getenv('MYSQL_MAX_CONNECTIONS', '10')),
            retry_count=int(os.getenv('MYSQL_RETRY_COUNT', '3')),
            retry_delay=int(os.getenv('MYSQL_RETRY_DELAY', '1')),
            ssl_disabled=os.getenv('MYSQL_SSL_DISABLED', 'false').lower() == 'true',
            ssl_ca=os.getenv('MYSQL_SSL_CA'),
            ssl_cert=os.getenv('MYSQL_SSL_CERT'),
            ssl_key=os.getenv('MYSQL_SSL_KEY'),
        )
    
    @classmethod
    def from_dict(cls, config_dict: Dict[str, Any]) -> 'DatabaseConfig':
        """
        从字典创建配置实例
        
        Args:
            config_dict: 包含配置参数的字典
            
        Returns:
            DatabaseConfig: 配置实例
        """
        return cls(**config_dict)
    
    @classmethod
    def from_json_file(cls, json_file: str) -> 'DatabaseConfig':
        """
        从 JSON 文件加载配置
        
        Args:
            json_file: JSON 配置文件路径
            
        Returns:
            DatabaseConfig: 配置实例
            
        Raises:
            FileNotFoundError: 当配置文件不存在时抛出异常
            json.JSONDecodeError: 当 JSON 格式无效时抛出异常
        """
        try:
            with open(json_file, 'r', encoding='utf-8') as f:
                config_data = json.load(f)
            
            # 如果配置文件包含 database 节点，使用该节点的配置
            if 'database' in config_data:
                config_data = config_data['database']
            
            return cls.from_dict(config_data)
        
        except FileNotFoundError:
            raise FileNotFoundError(f"配置文件不存在: {json_file}")
        except json.JSONDecodeError as e:
            raise json.JSONDecodeError(f"JSON 配置文件格式错误: {e}")
    
    def get_connection_params(self) -> Dict[str, Any]:
        """
        获取数据库连接参数
        
        Returns:
            Dict[str, Any]: 数据库连接参数字典
        """
        params = {
            'host': self.host,
            'port': self.port,
            'user': self.user,
            'password': self.password,
            'database': self.database,
            'charset': self.charset,
            'autocommit': self.autocommit,
            'raise_on_warnings': True,
            'sql_mode': 'STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO'
        }
        
        # SSL 配置
        if not self.ssl_disabled:
            ssl_config = {}
            if self.ssl_ca:
                ssl_config['ca'] = self.ssl_ca
            if self.ssl_cert:
                ssl_config['cert'] = self.ssl_cert
            if self.ssl_key:
                ssl_config['key'] = self.ssl_key
            
            if ssl_config:
                params['ssl'] = ssl_config
        else:
            params['ssl_disabled'] = True
        
        return params
    
    def get_pool_params(self) -> Dict[str, Any]:
        """
        获取连接池参数
        
        Returns:
            Dict[str, Any]: 连接池参数字典
        """
        return {
            'pool_size': self.pool_size,
            'max_overflow': self.max_overflow,
            'pool_timeout': self.pool_timeout,
            'pool_recycle': self.pool_recycle,
            'pool_reset_on_return': 'commit'
        }
    
    def to_connection_params(self) -> Dict[str, Any]:
        """
        转换为 mysql-connector-python 连接参数（向后兼容）
        
        Returns:
            Dict[str, Any]: 数据库连接参数字典
        """
        return self.get_connection_params()
    
    def __str__(self) -> str:
        """
        返回配置的字符串表示（隐藏敏感信息）
        
        Returns:
            str: 配置信息字符串
        """
        return (
            f"DatabaseConfig("
            f"host={self.host}, "
            f"port={self.port}, "
            f"user={self.user}, "
            f"database={self.database}, "
            f"timeout={self.connection_timeout}, "
            f"max_connections={self.max_connections}"
            f")"
        )
    
    def __repr__(self) -> str:
        """
        返回配置的详细表示
        
        Returns:
            str: 配置详细信息字符串
        """
        return self.__str__()


def validate_port(port: int) -> int:
    """
    独立的端口验证函数
    
    Args:
        port: 要验证的端口号
        
    Returns:
        int: 验证通过的端口号
        
    Raises:
        ValueError: 当端口号不在有效范围内时抛出异常
    """
    return DatabaseConfig._validate_port(port)


@dataclass
class LogConfig:
    """
    日志配置类
    
    管理日志记录相关的配置参数。
    """
    
    level: str = field(default="INFO")
    format: str = field(default="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    file_path: Optional[str] = field(default=None)
    max_file_size: int = field(default=10485760)  # 10MB
    backup_count: int = field(default=5)
    
    @classmethod
    def from_env(cls, env_file: Optional[str] = None) -> 'LogConfig':
        """
        从环境变量加载日志配置
        
        Args:
            env_file: .env 文件路径
            
        Returns:
            LogConfig: 日志配置实例
        """
        if env_file:
            load_dotenv(env_file)
        else:
            load_dotenv()
        
        return cls(
            level=os.getenv('LOG_LEVEL', 'INFO'),
            format=os.getenv('LOG_FORMAT', '%(asctime)s - %(name)s - %(levelname)s - %(message)s'),
            file_path=os.getenv('LOG_FILE_PATH'),
            max_file_size=int(os.getenv('LOG_MAX_FILE_SIZE', '10485760')),
            backup_count=int(os.getenv('LOG_BACKUP_COUNT', '5')),
        )


@dataclass
class MCPConfig:
    """
    MCP 服务器配置类
    
    管理 MCP 协议服务器相关的配置参数。
    """
    
    host: str = field(default="localhost")
    port: int = field(default=8080)
    max_connections: int = field(default=100)
    timeout: int = field(default=30)
    
    def __post_init__(self):
        """初始化后的验证处理"""
        self.port = DatabaseConfig._validate_port(self.port)
    
    @classmethod
    def from_env(cls, env_file: Optional[str] = None) -> 'MCPConfig':
        """
        从环境变量加载 MCP 配置
        
        Args:
            env_file: .env 文件路径
            
        Returns:
            MCPConfig: MCP 配置实例
        """
        if env_file:
            load_dotenv(env_file)
        else:
            load_dotenv()
        
        return cls(
            host=os.getenv('MCP_HOST', 'localhost'),
            port=int(os.getenv('MCP_PORT', '8080')),
            max_connections=int(os.getenv('MCP_MAX_CONNECTIONS', '100')),
            timeout=int(os.getenv('MCP_TIMEOUT', '30')),
        )


@dataclass
class Config:
    """
    主配置类
    
    整合所有配置模块，提供统一的配置管理接口。
    """
    
    database: DatabaseConfig = field(default_factory=DatabaseConfig)
    log: LogConfig = field(default_factory=LogConfig)
    mcp: MCPConfig = field(default_factory=MCPConfig)
    
    @classmethod
    def from_env(cls, env_file: Optional[str] = None) -> 'Config':
        """
        从环境变量加载完整配置
        
        Args:
            env_file: .env 文件路径
            
        Returns:
            Config: 完整配置实例
        """
        return cls(
            database=DatabaseConfig.from_env(env_file),
            log=LogConfig.from_env(env_file),
            mcp=MCPConfig.from_env(env_file),
        )
    
    @classmethod
    def from_json_file(cls, json_file: str) -> 'Config':
        """
        从 JSON 文件加载配置
        
        Args:
            json_file: JSON 配置文件路径
            
        Returns:
            Config: 配置实例
        """
        try:
            with open(json_file, 'r', encoding='utf-8') as f:
                config_data = json.load(f)
            
            database_config = DatabaseConfig()
            log_config = LogConfig()
            mcp_config = MCPConfig()
            
            if 'database' in config_data:
                database_config = DatabaseConfig.from_dict(config_data['database'])
            
            if 'log' in config_data:
                log_config = LogConfig(**config_data['log'])
            
            if 'mcp' in config_data:
                mcp_config = MCPConfig(**config_data['mcp'])
            
            return cls(
                database=database_config,
                log=log_config,
                mcp=mcp_config,
            )
        
        except FileNotFoundError:
            raise FileNotFoundError(f"配置文件不存在: {json_file}")
        except json.JSONDecodeError as e:
            raise json.JSONDecodeError(f"JSON 配置文件格式错误: {e}")


# 全局配置实例
_global_config: Optional[Config] = None


def get_config() -> Config:
    """
    获取全局配置实例
    
    Returns:
        Config: 全局配置实例
    """
    global _global_config
    if _global_config is None:
        _global_config = Config.from_env()
    return _global_config


def set_global_config(config: Config) -> None:
    """
    设置全局配置实例
    
    Args:
        config: 配置实例
    """
    global _global_config
    _global_config = config


def validate_port(port) -> bool:
    """
    验证端口号的有效性
    
    Args:
        port: 要验证的端口号（可以是int或str）
        
    Returns:
        bool: 端口号是否有效
    """
    try:
        port_int = int(port)
        return 1 <= port_int <= 65535
    except (ValueError, TypeError):
        return False


def load_config_from_env() -> DatabaseConfig:
    """
    从环境变量加载数据库配置
    
    Returns:
        DatabaseConfig: 数据库配置实例
        
    Raises:
        ConfigurationError: 当配置参数无效时抛出异常
    """
    try:
        # 获取端口号并验证
        port_str = os.getenv('MYSQL_PORT', '3306')
        if not validate_port(port_str):
            raise ConfigurationError(f"无效的端口号: {port_str}")
        
        # 获取连接池大小并验证
        pool_size_str = os.getenv('MYSQL_POOL_SIZE', '5')
        try:
            pool_size = int(pool_size_str)
            if pool_size <= 0:
                raise ConfigurationError(f"无效的连接池大小: {pool_size_str}")
        except ValueError:
            raise ConfigurationError(f"无效的连接池大小: {pool_size_str}")
        
        # 获取autocommit设置并验证
        autocommit_str = os.getenv('MYSQL_AUTOCOMMIT', 'true').lower()
        valid_true_values = ('true', '1', 'yes', 'on')
        valid_false_values = ('false', '0', 'no', 'off')
        
        if autocommit_str in valid_true_values:
            autocommit = True
        elif autocommit_str in valid_false_values:
            autocommit = False
        else:
            raise ConfigurationError(f"无效的布尔值: {autocommit_str}，期望值: true/false, 1/0, yes/no, on/off")
        
        # 创建数据库配置
        db_config = DatabaseConfig(
            host=os.getenv('MYSQL_HOST', 'localhost'),
            port=int(port_str),
            user=os.getenv('MYSQL_USER', ''),
            password=os.getenv('MYSQL_PASSWORD', ''),
            database=os.getenv('MYSQL_DATABASE', ''),
            charset=os.getenv('MYSQL_CHARSET', 'utf8mb4'),
            autocommit=autocommit,
            pool_size=pool_size,
            max_overflow=int(os.getenv('MYSQL_MAX_OVERFLOW', '10')),
            pool_timeout=int(os.getenv('MYSQL_POOL_TIMEOUT', '30')),
            pool_recycle=int(os.getenv('MYSQL_POOL_RECYCLE', '3600')),
            connection_timeout=int(os.getenv('MYSQL_TIMEOUT', '30')),
            max_connections=int(os.getenv('MYSQL_MAX_CONNECTIONS', '10')),
            retry_count=int(os.getenv('MYSQL_RETRY_COUNT', '3')),
            retry_delay=int(os.getenv('MYSQL_RETRY_DELAY', '1')),
            ssl_disabled=os.getenv('MYSQL_SSL_DISABLED', 'false').lower() == 'true',
            ssl_ca=os.getenv('MYSQL_SSL_CA'),
            ssl_cert=os.getenv('MYSQL_SSL_CERT'),
            ssl_key=os.getenv('MYSQL_SSL_KEY'),
        )
        
        return db_config
        
    except ValueError as e:
        raise ConfigurationError(f"配置参数类型错误: {e}")


def create_connection_string(config: DatabaseConfig) -> str:
    """
    创建数据库连接字符串
    
    Args:
        config: 数据库配置实例
        
    Returns:
        str: 数据库连接字符串
    """
    return (
        f"mysql://{config.user}:***@{config.host}:{config.port}/{config.database}"
    )